package com.rtf.framework.web.mvc.config.session;

import com.rtf.framework.common.config.AppProperties;
import com.rtf.framework.common.config.AppSecureProperties;
import com.rtf.framework.common.distributed.lock.DistributedLock;
import com.rtf.framework.common.distributed.lock.DistributedLocker;
import com.rtf.framework.common.secure.auth.AppUserDetailsDefault;
import com.rtf.framework.common.util.UtilAESCrypto;
import com.rtf.framework.common.util.UtilString;
import com.rtf.framework.web.common.filter.AppWebFilter;
import com.rtf.framework.web.common.interceptor.support.AppServletContextHolder;
import com.rtf.framework.web.mvc.config.secure.configurer.authentication.details.AppAuthenticationDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 *
 * 会话共享过滤器
 *
 * @author liuke
 * @date  2021/7/14 11:34
 * @version 1.0
*/
public class AppSessionLoginFilter extends AppWebFilter {

    private AppProperties appProperties;

    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DistributedLocker tributedLock;

    private AppSecureProperties appSecureProperties;

    private List<AppUserDetailConvertService> appUserDetailConvertService;

    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();

    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    public AppSessionLoginFilter(AppProperties appProperties , AppSecureProperties appSecureProperties, StringRedisTemplate stringRedisTemplate,List<AppUserDetailConvertService> appUserDetailConvertService) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.appProperties = appProperties;
        this.appSecureProperties = appSecureProperties;
        this.appUserDetailConvertService = appUserDetailConvertService;
    }

    @Override
    public boolean doFilterInternal(HttpServletRequest request, HttpServletResponse response ) {
        //将request存放到holder
        AppServletContextHolder.setServletRequest(request);
        //不开启session过滤，不做处理
        if(!appProperties.getEnableSessionFilter()){
            return true;
        }

        //如果没有cookie信息
        String sessionKey = request.getHeader(AppSessionConstants.SESSION_HEADER_KEY);
        sessionKey = UtilString.isNotBlank(sessionKey) ? sessionKey
                : request.getHeader(AppSessionConstants.SESSION_HEADER_TOKEN);
        //如果有cookie信息 如果有fs-ck或者token就取token
        if(request.getCookies()!=null&&UtilString.isBlank(sessionKey)) {
            //遍历cookie，获取信息
            for (Cookie cookie : request.getCookies()) {
                //如果有本项目cookie，直接跳转，由下一次校验
                if (cookie.getName().equals(appProperties.getSession().getCookieKey())) {
                    //如果已经包含本项目cookie，直接下一步处理
                    return true;
                }
            }
        }
        if(UtilString.isNotBlank(sessionKey)) {
            String userId = "";
            try {
                //解析指定cookie，解析出用户信息
                UtilAESCrypto.SessionValue sessionMessage = UtilAESCrypto.decryptSessionId(sessionKey);
                //判断这个sessionId是否过期
                if(sessionMessage.getTime()>System.currentTimeMillis()){
                    //如果id合法保存用户Id
                    userId=sessionMessage.getUserId();
                }
            }catch (Exception e){
                return true;
            }
            //判断是否存在用户id，如果不存在交给下一步处理
            if(UtilString.isNotBlank(userId)){
                //将用户Id暂存request
                request.setAttribute(AppSessionConstants.SESSION_LOGIN_KEY,userId);
                //查看redis中是否已存在当前用户的sessionId
                String sessionId=stringRedisTemplate.opsForValue().get(AppSessionConstants.SESSION_PREFIX+userId);
                //如果redis已经存在sessionId，这一步主要是app端用
                if(UtilString.isNotBlank(sessionId)){
                    //将redis缓存的sessionId暂存到request，并交给下一步处理
                    request.setAttribute(AppSessionConstants.SESSION_ID,sessionId);
                    return true;
                }
            }else {
                return true;
            }
            //获取分布式锁，保证一个用户在本系统只能有一个进程进行用户认证
            DistributedLock distributedLock = tributedLock.acquire(userId+appProperties.getName()+AppSessionConstants.SESSION_LOCK_KEY , 30*1000L ) ;
            //获取分布式锁失败,不进行授权认证，直接交由下一步操作
            if(distributedLock==null){
                return true;
            }
            //根据用户Id获取用户信息
            AppUserDetailsDefault userDetails = AppQueryUserDetail.getUserDetails(userId,appProperties,appSecureProperties,appUserDetailConvertService);
            //如果用户为空，跳过不做处理
            if(userDetails==null){
                return true;
            }
            //构建认证信息
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(userDetails,userDetails.getPassword(),authoritiesMapper.mapAuthorities(userDetails.getAuthorities()));
            //设置用户详细信息
            authentication.setDetails(new AppAuthenticationDetails(request));
            //用户认证
            sessionStrategy.onAuthentication(authentication,request,response);
            //将信息存放到容器
            SecurityContextHolder.getContext().setAuthentication(authentication);
            //创建session
            HttpSession session = request.getSession(true);
            //将生成的sessionId暂存到redis一份
            stringRedisTemplate.opsForValue().set(AppSessionConstants.SESSION_PREFIX+userId,session.getId(),60*60, TimeUnit.SECONDS);
            //将容器同步到session
            session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,SecurityContextHolder.getContext());
            //释放容器
            tributedLock.release( distributedLock ) ;
            SecurityContextHolder.clearContext();
        }
        return true;
    }

}
